package com.zkteco.fingerprint.service;

import com.zkteco.fingerprint.entity.FingerprintData;
import com.zkteco.fingerprint.repository.FingerprintDataRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import static com.zkteco.fingerprint.service.ZKFingerProxy.*;

/**
 * 指纹设备服务类
 * 负责指纹设备的连接、录入、验证等核心功能
 */
@Service
public class FingerprintDeviceService {
    
    @Autowired
    private FingerprintDataRepository fingerprintRepository;
    
    // ================== 设备相关属性 ==================
    private long deviceHandle = 0;
    private long databaseHandle = 0;
    private boolean isConnected = false;
    
    // ================== 图像相关属性 ==================
    private int imageWidth = 0;
    private int imageHeight = 0;
    private byte[] imageBuffer = null;
    
    // ================== 指纹模板相关属性 ==================
    private byte[] templateBuffer = new byte[2048];
    private int[] templateLength = new int[1];
    private byte[][] enrollTemplates = new byte[3][2048];
    private String lastEnrolledTemplate = null;
    
    // ================== 状态控制属性 ==================
    private AtomicBoolean isCapturing = new AtomicBoolean(false);
    private boolean isEnrollMode = false;
    private int enrollStep = 0;
    private int nextFingerprintId = 1;
    
    // ================== 当前录入用户信息 ==================
    private String currentUserId = null;
    private String currentUserName = null;
    
    // ================== 工作线程 ==================
    private Thread workThread = null;
    private boolean shouldStop = true;
    
    // ================== 回调函数 ==================
    private Consumer<String> messageCallback;
    private Consumer<byte[]> imageCallback;
    private Consumer<String> templateCallback;
    
    // ================== 配置常量 ==================
    private static final int ENROLL_REQUIRED_TIMES = 3;
    private static final int DEFAULT_QUALITY_SCORE = 85;
    private static final int LIVENESS_DETECTION_ON = 1;
    
    // ==================== 公共接口方法 ====================
    
    /**
     * 连接指纹设备
     */
    public boolean connectDevice() {
        if (isConnected) {
            sendMessage("[警告] 设备已连接");
            return true;
        }
        
        try {
            resetEnrollmentState();
            
            if (!initializeSDK()) {
                return false;
            }
            
            if (!detectAndOpenDevice()) {
                return false;
            }
            
            if (!initializeDatabase()) {
                return false;
            }
            
            if (!getDeviceParameters()) {
                return false;
            }
            
            setupImageBuffer();
            startWorkThread();
            
            isConnected = true;
            sendMessage("[成功] 设备连接成功！图像尺寸: " + imageWidth + "x" + imageHeight);
            return true;
            
        } catch (Exception e) {
            sendMessage("[错误] 设备连接异常: " + e.getMessage());
            return false;
        }
    }
    
    /**
     * 断开指纹设备
     */
    public void disconnectDevice() {
        try {
            stopAllOperations();
            stopWorkThread();
            cleanupResources();
            
            isConnected = false;
            sendMessage("[信息] 设备已断开连接");
            
        } catch (Exception e) {
            sendMessage("[错误] 断开设备异常: " + e.getMessage());
        }
    }
    
    /**
     * 开始指纹录入
     */
    public CompletableFuture<String> startEnrollment(String userId, String userName) {
        return CompletableFuture.supplyAsync(() -> {
            if (!isConnected) {
                return "设备未连接";
            }
            
            if (fingerprintRepository.existsByUserId(userId)) {
                sendMessage("[警告] 用户ID " + userId + " 已存在，请使用不同的用户ID");
                return "用户ID已存在";
            }
            
            try {
                startEnrollmentProcess(userId, userName);
                
                // 等待录入完成或被取消
                while (isCapturing.get() && isEnrollMode) {
                    Thread.sleep(100);
                }
                
                return handleEnrollmentResult();
                
            } catch (Exception e) {
                sendMessage("[错误] 录入异常: " + e.getMessage());
                return null;
            } finally {
                stopEnrollmentProcess();
            }
        });
    }
    
    /**
     * 开始连续指纹验证
     */
    public CompletableFuture<String> startContinuousVerification(String templateToMatch) {
        return CompletableFuture.supplyAsync(() -> {
            if (!isConnected) {
                return "设备未连接";
            }
            
            try {
                startVerificationProcess(templateToMatch);
                
                // 连续验证模式，直到用户停止
                while (isCapturing.get()) {
                    Thread.sleep(100);
                }
                
                sendMessage("[信息] 连续验证模式已停止");
                return "连续验证已停止";
                
            } catch (Exception e) {
                sendMessage("[错误] 验证异常: " + e.getMessage());
                return "验证异常: " + e.getMessage();
            } finally {
                stopVerificationProcess();
            }
        });
    }
    
    /**
     * 停止当前操作
     */
    public void stopCurrentOperation() {
        isCapturing.set(false);
        sendMessage("[信息] 操作已停止");
    }
    
    /**
     * 获取设备连接状态
     */
    public boolean isDeviceConnected() {
        return isConnected;
    }
    
    // ==================== 回调设置方法 ====================
    
    public void setMessageCallback(Consumer<String> callback) {
        this.messageCallback = callback;
    }
    
    public void setImageCallback(Consumer<byte[]> callback) {
        this.imageCallback = callback;
    }
    
    public void setTemplateCallback(Consumer<String> callback) {
        this.templateCallback = callback;
    }
    
    // ==================== 设备初始化私有方法 ====================
    
    private boolean initializeSDK() {
        sendMessage("[信息] 正在初始化指纹识别SDK...");
        
        if (ZKFP_ERR_OK != Init()) {
            sendMessage("[错误] SDK初始化失败！");
            return false;
        }
        return true;
    }
    
    private boolean detectAndOpenDevice() {
        int deviceCount = GetDeviceCount();
        if (deviceCount < 0) {
            sendMessage("[错误] 未检测到指纹识别设备！请检查设备连接。");
            Terminate();
            return false;
        }
        
        sendMessage("[信息] 检测到 " + deviceCount + " 个指纹识别设备");
        
        if (0 == (deviceHandle = OpenDevice(0))) {
            sendMessage("[错误] 打开设备失败");
            Terminate();
            return false;
        }
        return true;
    }
    
    private boolean initializeDatabase() {
        if (0 == (databaseHandle = DBInit())) {
            sendMessage("[错误] 数据库初始化失败");
            disconnectDevice();
            return false;
        }
        return true;
    }
    
    private boolean getDeviceParameters() {
        byte[] paramValue = new byte[4];
        int[] size = new int[1];
        
        // 获取图像宽度
        size[0] = 4;
        GetParameters(deviceHandle, 1, paramValue, size);
        imageWidth = byteArrayToInt(paramValue);
        
        // 获取图像高度
        size[0] = 4;
        GetParameters(deviceHandle, 2, paramValue, size);
        imageHeight = byteArrayToInt(paramValue);
        
        return imageWidth > 0 && imageHeight > 0;
    }
    
    private void setupImageBuffer() {
        imageBuffer = new byte[imageWidth * imageHeight];
    }
    
    private void startWorkThread() {
        shouldStop = false;
        workThread = new Thread(this::workThreadMain);
        workThread.start();
    }
    
    // ==================== 录入相关私有方法 ====================
    
    private void startEnrollmentProcess(String userId, String userName) {
        sendMessage("[信息] 开始录入指纹，用户: " + userName);
        sendMessage("[信息] 请按压手指3次...");
        
        currentUserId = userId;
        currentUserName = userName;
        enrollStep = 0;
        isEnrollMode = true;
        isCapturing.set(true);
    }
    
    private String handleEnrollmentResult() {
        if (!isCapturing.get()) {
            sendMessage("[信息] 指纹录入已取消");
            return null;
        }
        
        if (enrollStep >= ENROLL_REQUIRED_TIMES) {
            sendMessage("[成功] 指纹录入完成！");
            return lastEnrolledTemplate;
        } else {
            sendMessage("[错误] 指纹录入未完成");
            return null;
        }
    }
    
    private void stopEnrollmentProcess() {
        isCapturing.set(false);
        isEnrollMode = false;
        currentUserId = null;
        currentUserName = null;
    }
    
    private void resetEnrollmentState() {
        isEnrollMode = false;
        nextFingerprintId = 1;
        enrollStep = 0;
    }
    
    // ==================== 验证相关私有方法 ====================
    
    private void startVerificationProcess(String templateToMatch) {
        sendMessage("[信息] 开始连续指纹验证模式，请按压手指进行验证...");
        sendMessage("[提示] 您可以连续验证多次，点击停止按钮结束验证");
        
        lastEnrolledTemplate = templateToMatch;
        isCapturing.set(true);
        isEnrollMode = false;
    }
    
    private void stopVerificationProcess() {
        isCapturing.set(false);
        lastEnrolledTemplate = null;
    }
    
    // ==================== 工作线程相关方法 ====================
    
    private void workThreadMain() {
        while (!shouldStop) {
            templateLength[0] = 2048;
            int result = AcquireFingerprint(deviceHandle, imageBuffer, templateBuffer, templateLength);
            
            if (result == 0) {
                if (isLivenessDetectionPassed()) {
                    processFingerprint();
                }
            }
            
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
    
    private boolean isLivenessDetectionPassed() {
        if (LIVENESS_DETECTION_ON != 1) {
            return true;
        }
        
        byte[] paramValue = new byte[4];
        int[] size = new int[1];
        size[0] = 4;
        
        int result = GetParameters(deviceHandle, 2004, paramValue, size);
        int fakeStatus = byteArrayToInt(paramValue);
        
        if (result == 0 && (byte)(fakeStatus & 31) != 31) {
            sendMessage("[警告] 检测到可疑指纹，可能是假指纹！");
            return false;
        }
        return true;
    }
    
    private void processFingerprint() {
        processImageData();
        processTemplateData();
    }
    
    private void processImageData() {
        if (imageCallback != null) {
            try {
                byte[] bmpData = convertToBMP(imageBuffer, imageWidth, imageHeight);
                imageCallback.accept(bmpData);
            } catch (Exception e) {
                sendMessage("[错误] 图像转换失败: " + e.getMessage());
            }
        }
    }
    
    private void processTemplateData() {
        if (isEnrollMode) {
            handleEnrollmentTemplate();
        } else if (isCapturing.get() && !isEnrollMode) {
            handleVerificationTemplate();
        }
    }
    
    // ==================== 录入模板处理 ====================
    
    private void handleEnrollmentTemplate() {
        // 检查是否已录入该指纹
        int[] fingerprintId = new int[1];
        int[] score = new int[1];
        int result = DBIdentify(databaseHandle, templateBuffer, fingerprintId, score);
        
        if (result == 0) {
            sendMessage("[警告] 该指纹已被录入，指纹ID: " + fingerprintId[0] + "，取消录入");
            stopEnrollmentProcess();
            return;
        }
        
        // 检查是否为同一根手指
        if (enrollStep > 0 && DBMatch(databaseHandle, enrollTemplates[enrollStep-1], templateBuffer) <= 0) {
            sendMessage("[警告] 请按压同一根手指进行录入");
            return;
        }
        
        // 保存当前模板
        System.arraycopy(templateBuffer, 0, enrollTemplates[enrollStep], 0, 2048);
        enrollStep++;
        
        if (enrollStep == ENROLL_REQUIRED_TIMES) {
            completeEnrollment();
        } else {
            sendMessage("[信息] 请继续按压手指，还需要 " + (ENROLL_REQUIRED_TIMES - enrollStep) + " 次");
        }
    }
    
    private void completeEnrollment() {
        int[] mergedTemplateLength = new int[1];
        mergedTemplateLength[0] = 2048;
        byte[] mergedTemplate = new byte[mergedTemplateLength[0]];
        
        int mergeResult = DBMerge(databaseHandle, enrollTemplates[0], enrollTemplates[1], enrollTemplates[2], mergedTemplate, mergedTemplateLength);
        int addResult = DBAdd(databaseHandle, nextFingerprintId, mergedTemplate);
        
        if (mergeResult == 0 && addResult == 0) {
            nextFingerprintId++;
            String base64Template = BlobToBase64(mergedTemplate, mergedTemplateLength[0]);
            lastEnrolledTemplate = base64Template;
            
            if (templateCallback != null) {
                templateCallback.accept(base64Template);
            }
            
            sendMessage("[成功] 指纹录入成功！指纹ID: " + (nextFingerprintId - 1));
            
            // 保存到数据库
            if (currentUserId != null && currentUserName != null) {
                saveFingerprintToDatabase(currentUserId, currentUserName, base64Template, DEFAULT_QUALITY_SCORE);
            }
            
            isEnrollMode = false;
        } else {
            sendMessage("[错误] 指纹录入失败，错误代码: " + mergeResult + "/" + addResult);
        }
    }
    
    // ==================== 验证模板处理 ====================
    
    private void handleVerificationTemplate() {
        if (lastEnrolledTemplate != null) {
            byte[] targetTemplate = Base64.getDecoder().decode(lastEnrolledTemplate);
            int matchScore = DBMatch(databaseHandle, targetTemplate, templateBuffer);
            
            if (matchScore > 0) {
                sendMessage("[成功] 验证成功！匹配分数: " + matchScore + " - 请继续验证或点击停止");
            } else {
                sendMessage("[失败] 验证失败，指纹不匹配 - 请重试或点击停止");
            }
        } else {
            sendMessage("[警告] 请先录入指纹模板！");
            isCapturing.set(false);
        }
    }
    
    // ==================== 数据库操作方法 ====================
    
    @Transactional
    private void saveFingerprintToDatabase(String userId, String userName, String template, int qualityScore) {
        try {
            FingerprintData fingerprintData = new FingerprintData();
            fingerprintData.setUserId(userId);
            fingerprintData.setUserName(userName);
            fingerprintData.setTemplateData(template);
            fingerprintData.setQualityScore(qualityScore);
            fingerprintData.setFingerIndex(1);
            fingerprintData.setStatus(1);
            
            fingerprintRepository.save(fingerprintData);
            sendMessage("[数据库] 指纹数据已保存到MySQL数据库");
            sendMessage("[数据库] 用户ID: " + userId + ", 用户名: " + userName);
            
        } catch (Exception e) {
            sendMessage("[错误] 保存指纹到数据库失败: " + e.getMessage());
        }
    }
    
    // ==================== 清理和停止方法 ====================
    
    private void stopAllOperations() {
        isCapturing.set(false);
        isEnrollMode = false;
        shouldStop = true;
    }
    
    private void stopWorkThread() {
        if (workThread != null && workThread.isAlive()) {
            try {
                workThread.join(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            workThread = null;
        }
    }
    
    private void cleanupResources() {
        if (databaseHandle != 0) {
            DBFree(databaseHandle);
            databaseHandle = 0;
        }
        
        if (deviceHandle != 0) {
            CloseDevice(deviceHandle);
            deviceHandle = 0;
        }
        
        Terminate();
    }
    
    // ==================== 工具方法 ====================
    
    private void sendMessage(String message) {
        System.out.println(message);
        if (messageCallback != null) {
            messageCallback.accept(message);
        }
    }
    
    private static int byteArrayToInt(byte[] bytes) {
        int number = bytes[0] & 0xFF;
        number |= ((bytes[1] << 8) & 0xFF00);
        number |= ((bytes[2] << 16) & 0xFF0000);
        number |= ((bytes[3] << 24) & 0xFF000000);
        return number;
    }
    
    // ==================== 图像处理方法 ====================
    
    private byte[] convertToBMP(byte[] grayData, int width, int height) {
        int fileSize = 54 + (width * height * 3);
        byte[] bmpData = new byte[fileSize];
        
        // BMP文件头
        bmpData[0] = 'B'; bmpData[1] = 'M';
        writeInt(bmpData, 2, fileSize);
        writeInt(bmpData, 6, 0);
        writeInt(bmpData, 10, 54);
        
        // BMP信息头
        writeInt(bmpData, 14, 40);
        writeInt(bmpData, 18, width);
        writeInt(bmpData, 22, height);
        writeShort(bmpData, 26, 1);
        writeShort(bmpData, 28, 24);
        writeInt(bmpData, 30, 0);
        writeInt(bmpData, 34, width * height * 3);
        writeInt(bmpData, 38, 2835);
        writeInt(bmpData, 42, 2835);
        writeInt(bmpData, 46, 0);
        writeInt(bmpData, 50, 0);
        
        // 像素数据
        int dataOffset = 54;
        for (int y = height - 1; y >= 0; y--) {
            for (int x = 0; x < width; x++) {
                int grayValue = grayData[y * width + x] & 0xFF;
                bmpData[dataOffset++] = (byte) grayValue;
                bmpData[dataOffset++] = (byte) grayValue;
                bmpData[dataOffset++] = (byte) grayValue;
            }
        }
        
        return bmpData;
    }
    
    private void writeInt(byte[] data, int offset, int value) {
        data[offset] = (byte) (value & 0xFF);
        data[offset + 1] = (byte) ((value >> 8) & 0xFF);
        data[offset + 2] = (byte) ((value >> 16) & 0xFF);
        data[offset + 3] = (byte) ((value >> 24) & 0xFF);
    }
    
    private void writeShort(byte[] data, int offset, int value) {
        data[offset] = (byte) (value & 0xFF);
        data[offset + 1] = (byte) ((value >> 8) & 0xFF);
    }
}